package jp.dodododo.reloadable.servlet.impl;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import jp.dodododo.reloadable.servlet.ServletWrapper;
import jp.dodododo.reloadable.servlet.ServletWrapperContainer;
import jp.dodododo.reloadable.util.ContextClassLoaderUtils;
import jp.dodododo.reloadable.util.InitializeUtils;

public class ServletWrapperDelegator extends GenericServlet implements ServletWrapper {
	private static final long serialVersionUID = 1L;

	private ServletConfig config;

	private String targetClass;

	private boolean isInitialized = false;

	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		super.init(servletConfig);
		this.config = servletConfig;
		targetClass = servletConfig.getInitParameter(ServletWrapper.class.getName() + ".targetClass");
		ServletWrapperContainer.register(this);
	}

	@Override
	public void destroy() {
		super.destroy();
		ServletWrapperContainer.remove(this);
		config = null;
		targetClass = null;
		isInitialized = false;
	}

	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		Thread.currentThread().setContextClassLoader(ContextClassLoaderUtils.getContextClassLoader());
		try {
			if (isInitialized == false) {
				initTarget();
			}
			target.service(request, response);
		} catch (ServletException e) {
			e.printStackTrace();
			throw e;
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		} catch (RuntimeException e) {
			e.printStackTrace();
			throw e;
		} catch (Error e) {
			e.printStackTrace();
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} finally {
			Thread.currentThread().setContextClassLoader(ContextClassLoaderUtils.getContextClassLoader());
		}
	}

	private Servlet target = null;

	public void initTarget() throws Exception {
		ClassLoader cl = Thread.currentThread().getContextClassLoader();
		Class<?> clazz = Class.forName(targetClass, true, cl);
		target = Servlet.class.cast(clazz.newInstance());

		long start = System.currentTimeMillis();
		target.init(config);
		isInitialized = true;
		long end = System.currentTimeMillis();
		InitializeUtils.initializeInfoLog(targetClass, start, end);
	}

	public void destroyTarget() {
		try {
			if (target != null) {
				target.destroy();
			}
		} finally {
			target = null;
			isInitialized = false;
		}
	}

}
